home *** CD-ROM | disk | FTP | other *** search
- /*
- File: VBLSnippet.c
-
- Contains: A simple example of a persistent VBL written in C that
- works with 68K or PowerPC.
-
- Written by: Jim Luther (Based on the VBL code from the Technical Note
- "TB 35 - MultiFinder Miscellanea".)
-
- Copyright: Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
- 7/27/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
-
- */
-
- #include <Types.h>
- #include <Memory.h>
- #include <QuickDraw.h>
- #include <Fonts.h>
- #include <Windows.h>
- #include <Menus.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Events.h>
- #include <TextUtils.h>
- #include <Retrace.h>
- #include <LowMem.h>
- #include <Gestalt.h>
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** Define a struct to keep track of what we need in the VBL. Put theVBLTask
- ** into the struct first because its address will be passed to our VBL task
- ** in A0.
- */
- struct VBLRec
- {
- VBLTask theVBLTask; /* the VBL task itself */
- #if !GENERATINGCFM
- long VBLA5; /* saved CurrentA5 where we can find it for 68K code */
- #endif
- };
- typedef struct VBLRec VBLRec;
- typedef struct VBLRec *VBLRecPtr;
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** Constants used in sample
- */
-
- enum
- {
- kInterval = 6, /* VBL interval */
- rInfoDialog = 140, /* DLOG resource ID */
- rStatTextItem = 1 /* item number of counter field in dialog */
- };
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** Prototypes
- */
-
- void DoVBL(VBLRecPtr recPtr);
-
- #if GENERATINGCFM
- void StartVBL(VBLTaskPtr vblTaskPtr); /* Under CFM, we're passed the VBL task pointer */
- #else
- void StartVBL(void); /* Otherwise, we'll have to get it out of register A0 */
- #endif
-
- OSErr NewPersistentVBLProc(ProcPtr userRoutine, VBLUPP *theVBLUPP);
- void DisposePersistentVBLProc(VBLUPP theVBLUPP);
- void main(void);
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** A global which will be referenced from our VBL Task and the test program
- */
-
- long gCounter; /* Counter incremented each time our VBL gets called */
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** DoVBL is called only by StartVBL()
- */
- void DoVBL(VBLRecPtr recPtr)
- {
- gCounter++; /* Show we can set a global */
- recPtr->theVBLTask.vblCount = kInterval; /* Set ourselves to run again */
- }
-
- /*----------------------------------------------------------------------------*/
-
- #if !GENERATINGCFM
- /*
- ** GetVBLRec returns the address of the VBLRec associated with our VBL task.
- ** This works because on entry into the VBL task, A0 points to the theVBLTask
- ** field in the VBLRec record, which is the first field in the record and that
- ** is the address we return. Note that this method works whether the VBLRec
- ** is allocated globally, in the heap (as long as the record is locked in
- ** memory) or if it is allocated on the stack as is the case in this example.
- ** In the latter case this is OK as long as the procedure which installed the
- ** task does not exit while the task is running. This trick allows us to get
- ** to the saved A5, but it could also be used to get to anything we wanted to
- ** store in the record.
- */
- extern VBLRecPtr GetVBLRec(void)
- = 0x2008; /* MOVE.L A0,D0 */
- #endif
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** This is the actual VBL task code. It uses GetVBLRec to get our VBL record
- ** and properly set up A5 (non-CFM only).
- ** Because of the vagaries of C optimization, it calls a separate routine to
- ** actually access global variables. See "OV 10 - Setting and Restoring A5"
- ** for the reasons for this, as well as for a description of SetA5.
- */
-
- #if GENERATINGCFM
- void StartVBL(VBLTaskPtr vblTaskPtr)
- {
- DoVBL((VBLRecPtr)vblTaskPtr); /* Call another routine to do actual work */
- }
- #else
- void StartVBL()
- {
- long curA5;
- VBLRecPtr recPtr;
-
- recPtr = GetVBLRec(); /* First get our record */
- curA5 = SetA5(recPtr->VBLA5); /* Get the saved A5 */
- /* Now we can access globals */
-
- DoVBL(recPtr); /* Call another routine to do actual work */
-
- (void) SetA5(curA5); /* Restore old A5 */
- }
- #endif
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** NewPersistentVBLProc allocates the VBLUPP in the System heap so the VBL
- ** will be persistent.
- */
- OSErr NewPersistentVBLProc(ProcPtr userRoutine, VBLUPP *theVBLUPP)
- {
- #if GENERATINGCFM
- OSErr result;
- THz savedZone;
-
- savedZone = GetZone();
- SetZone(SystemZone());
- *theVBLUPP = NewVBLProc(userRoutine);
- result = MemError();
- SetZone(savedZone);
- return ( result );
- #else
- enum
- {
- kJMPInstr = 0x4ef9,
- kJMPSize = 6
- };
- OSErr result;
- Ptr sysHeapPtr;
-
- sysHeapPtr = NewPtrSys(kJMPSize);
- result = MemError();
- if ( result == noErr )
- {
- *(short *)sysHeapPtr = kJMPInstr;
- *(ProcPtr *)(sysHeapPtr+2) = userRoutine;
- FlushCodeCacheRange(sysHeapPtr, kJMPSize);
- *theVBLUPP = (VBLUPP)sysHeapPtr;
- }
- return ( result );
- #endif
- }
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** DisposePersistentVBLProc frees up the memory used for the VBLUPP.
- */
- void DisposePersistentVBLProc(VBLUPP theVBLUPP)
- {
- #if GENERATINGCFM
- THz savedZone;
-
- savedZone = GetZone();
- SetZone(SystemZone());
- DisposeRoutineDescriptor(theVBLUPP);
- SetZone(savedZone);
- #else
- DisposePtr((Ptr)theVBLUPP);
- #endif
- }
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** Create a dialog just to demonstrate that the global variable
- ** is being updated by the VBL Task. Before installing the VBL, we store
- ** our A5 in the actual VBL Task record, using SetCurrentA5 described in
- ** OV 10 - Setting and Restoring A5. We'll run the VBL, showing the counter
- ** being incremented, until the mouse button is clicked. Then we remove
- ** the VBL Task, close the dialog, and remove the mouse down events to
- ** prevent the application from being inadvertently switched by MultiFinder.
- */
- void main (void)
- {
- VBLRec theVBLRec;
- DialogPtr infoDPtr;
- DialogRecord infoDStorage;
- Str255 numStr = "\pTest";
- OSErr theErr;
- Handle theItemHandle;
- short theItemType;
- Rect theRect;
- long lastCount = 0;
-
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(NULL);
- InitCursor();
- MaxApplZone();
-
- #if !GENERATINGCFM
- /* Store the current value of A5 in the VBLA5 field if not CFM. */
- theVBLRec.VBLA5 = SetCurrentA5 ();
- #endif
-
- gCounter = 0; /* Initialize our global counter */
-
- /* Put up the dialog */
- infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
- DrawDialog (infoDPtr);
- GetDialogItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, &theRect);
-
- /* Set the address of our routine */
- theErr = NewPersistentVBLProc((ProcPtr)StartVBL, &theVBLRec.theVBLTask.vblAddr);
- theVBLRec.theVBLTask.vblCount = kInterval; /* Frequency of task, in ticks */
- theVBLRec.theVBLTask.qType = vType; /* qElement is a VBL task */
- theVBLRec.theVBLTask.vblPhase = 0;
-
- /* Now install the VBL task */
- theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
-
- /* Display the counter until the mouse button is pushed */
- if ( theErr == noErr )
- {
- do
- {
- if (gCounter != lastCount)
- {
- lastCount = gCounter;
- NumToString(gCounter, numStr);
- SetDialogItemText(theItemHandle, numStr);
- }
- } while ( !Button () );
- theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
- DisposePersistentVBLProc(theVBLRec.theVBLTask.vblAddr); /* Dispose of the memory */
- }
-
- /* Finish up */
- CloseDialog (infoDPtr); /* Get rid of our dialog */
- FlushEvents (mDownMask, 0); /* Flush all mouse down events */
- }
-